// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CachingService.cs" company="Scrum for Team System">
//   None
// </copyright>
// <summary>
//   Defines the CachingService type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ScrumForTeamSystem.EventService.Services
{
    using System;
    using System.Globalization;

    using Microsoft.Practices.EnterpriseLibrary.Caching;
    using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;

    using ScrumForTeamSystem.EventService.Infrastructure;
    using ScrumForTeamSystem.EventService.Infrastructure.Interfaces;
    using ScrumForTeamSystem.EventService.Services.Properties;

    /// <summary>
    /// Defines the CachingService type.
    /// </summary>
    internal class CachingService : ICachingService
    {
        /// <summary>
        /// The caching duration
        /// </summary>
        private readonly int cacheDuration;

        /// <summary>
        /// Instance of the cache manager service
        /// </summary>
        private readonly CacheManager cacheManager;

        /// <summary>
        /// Initializes a new instance of the <see cref="CachingService"/> class.
        /// </summary>
        public CachingService()
        {
            this.cacheManager = CacheFactory.GetCacheManager();
            this.cacheDuration = Settings.Default.CacheDurationSeconds;
        }

        /// <summary>
        /// Occurs when [action] event raised.
        /// </summary>
        public event EventHandler<ActionEventArgs> Message;

        /// <summary>
        /// Occurs when [exception action] event raised.
        /// </summary>
        public event EventHandler<ExceptionActionEventArgs> ExceptionMessage;

        /// <summary>
        /// Gets a value indicating whether this instance is subscribbed.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is subscribbed; otherwise, <c>false</c>.
        /// </value>
        private bool IsSubscribbed
        {
            get
            {
                return this.Message != null && this.ExceptionMessage != null;
            }
        }

        /// <summary>
        /// Caches the object.
        /// </summary>
        /// <typeparam name="T">The type of the item to cahce</typeparam>
        /// <param name="cacheId">The cache id.</param>
        /// <param name="item">The item to cache.</param>
        public void CacheObject<T>(string cacheId, T item)
        {
            if (this.ContainsKey(cacheId))
            {
                this.cacheManager.Remove(cacheId);
            }

            this.cacheManager.Add(
                    cacheId,
                    item,
                    CacheItemPriority.Normal,
                    null,
                    new SlidingTime(TimeSpan.FromSeconds(this.cacheDuration)));

            if (!this.IsSubscribbed)
            {
                return;
            }

            var actionEventArgs = new ActionEventArgs(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        "Object of type '{0}' added to cache under id '{1}'",
                        new object[] { item.GetType().FullName, cacheId }));

            this.Message(this, actionEventArgs);
        }

        /// <summary>
        /// Determines whether the specified cache id contains key.
        /// </summary>
        /// <param name="cacheId">The cache id.</param>
        /// <returns>
        /// <c>true</c> if the cache contains the specified key; otherwise, <c>false</c>.
        /// </returns>
        public bool ContainsKey(string cacheId)
        {
            return this.cacheManager.Contains(cacheId);
        }

        /// <summary>
        /// Gets the cached item.
        /// </summary>
        /// <typeparam name="T">The type of the item to cahce</typeparam>
        /// <param name="cacheId">The cache id.</param>
        /// <returns>The cached item</returns>
        /// <exception cref="ApplicationException">
        /// </exception>
        public T GetCachedItem<T>(string cacheId)
        {
            var output = default(T);

            if (this.ContainsKey(cacheId))
            {
                try
                {
                    if (!this.IsTypeOf(cacheId, typeof(T)))
                    {
                        var message = String.Format(
                                CultureInfo.CurrentUICulture,
                                "Get Cached Item Error: The cached item with Id '{0}' is not of the specified type '{1}'",
                                new object[] { cacheId, typeof(T).FullName });

                        throw new ServiceException("CachingService", message);
                    }

                    output = (T)this.cacheManager[cacheId];
                }
                catch (Exception ex)
                {
                    if (this.IsSubscribbed)
                    {
                        this.ExceptionMessage(this, new ExceptionActionEventArgs(ex));
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            return output;
        }

        /// <summary>
        /// Determines whether [the specified cache id] [is type of].
        /// </summary>
        /// <param name="cacheId">The cache id.</param>
        /// <param name="type">The comparrision type.</param>
        /// <returns>
        /// <c>true</c> if [the specified cache id] [is type of]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsTypeOf(string cacheId, Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            return this.ContainsKey(cacheId) && type.IsAssignableFrom(this.cacheManager[cacheId].GetType());
        }
    }
}